package com.yifeng.repo.controller.excel.simple.download;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.gomcarter.frameworks.base.exception.CustomException;
import com.yifeng.repo.controller.excel.simple.download.annotation.ResponseFile;
import com.yifeng.repo.controller.excel.simple.download.model.ExcelContent;
import com.yifeng.repo.controller.excel.simple.download.model.ExcelResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.http.ContentDisposition;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StreamUtils;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.method.support.ModelAndViewContainer;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;
import java.util.Optional;

/**
 * 简易导出
 * @author zyh
 * create on 2023/3/10 10:45
 */
@Slf4j
public class DownloadReturnValueHandler implements HandlerMethodReturnValueHandler {

    @Override
    public boolean supportsReturnType(MethodParameter returnType) {
        return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseFile.class) ||
                returnType.hasMethodAnnotation(ResponseFile.class));
    }

    @Override
    @SuppressWarnings("all")
    public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer,
                                  NativeWebRequest webRequest) throws Exception {
        HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
        Assert.state(response != null, "response is null");
        if (ObjectUtils.isEmpty(returnValue)) {
            log.warn("返回值为空");
            return;
        }
        ResponseFile responseFile = returnType.getMethodAnnotation(ResponseFile.class);
        Assert.state(responseFile != null, "导出接口必须添加@ResonseFile注解");
        Assert.state(returnValue instanceof ExcelResult, "导出接口返回值必须为ExcelResult类型");
        ExcelResult excelResult = ExcelResult.class.cast(returnValue);
        List<ExcelContent> excelContents = excelResult.getExcelContents();
        String filename = excelResult.getFileName() + ExcelTypeEnum.XLSX.getValue();
        if(ObjectUtils.isEmpty(excelContents)) {
           log.warn("文件内容为空, 文件名={}", filename);
           return;
        }
        response.setContentType(new MediaType("application", "vnd.ms-excel", StandardCharsets.UTF_8).toString());
        response.setCharacterEncoding(StandardCharsets.UTF_8.name());
        response.setHeader(HttpHeaders.CONTENT_DISPOSITION, ContentDisposition
                .builder("attachment")
                .filename(filename, StandardCharsets.UTF_8)
                .build()
                .toString());
        //在响应头中添加导出条数
        long count = 0;
        for (ExcelContent excelContent : excelContents) {
            if(!ObjectUtils.isEmpty(excelContent.getData())) {
                count += excelContent.getData().size();
            }
        }
        response.addHeader("Content-Size", String.valueOf(count));
        response.setHeader("Access-Control-Expose-Headers","Content-Size");
        Optional.ofNullable(excelContents).ifPresent(contents -> {
            // sorted by sheetNo
            Collections.sort(contents, (c1, c2) -> c1.getSheetNo() - c2.getSheetNo());
            try (ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).build()) {
                contents.forEach(content -> {
                    WriteSheet writeSheet = EasyExcel.writerSheet(content.getSheetNo(), content.getSheetName())
                            .head(content.getHead()).build();
                    excelWriter.write(content.getData(), writeSheet);
                });
            } catch (IOException e) {
                log.error("文件导出异常, 文件名={}, 原因={}", filename, e);
                throw new CustomException("文件导出异常");
            }
        });
    }

    public void doPrint(HttpServletResponse response) throws IOException {
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response.setCharacterEncoding(StandardCharsets.UTF_8.name());
        response.addHeader("content-size", "0");
        StreamUtils.copy("数据为空", StandardCharsets.UTF_8, response.getOutputStream());
    }
}
